﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Red.Controls.ViewModels;
using Red.Core.Containers;

namespace Red.Launcher.ViewModels.Workspaces.Admin.Tree
{
    public class Item : RedTreeItem
    {
        #region Private Data
        
        private Database.Record _data;
        private string _customIcon;
        private string _currentIcon;
        private bool _buildOnChildCollectionChanged;

        #endregion Private Data

        #region Public Properties

        public Database.Record Data
        {
            get
            {
                Database.IRecordWrapper wrapper = _data as Database.IRecordWrapper;

                if ( wrapper != null )
                {
                    return wrapper.ActualRecord;
                }

                return _data;
            }
            private set { _data = value; }
        }

        public static Item SelectedItem { get; set; }
        public static Database.Record SelectedData { get; set; }
        public override bool IsExpandable => true;

        public bool BuildOnChildCollectionChanged
        {
            get { return _buildOnChildCollectionChanged; }
            set
            {
                if( value && !_buildOnChildCollectionChanged )
                {
                    Data.OnChildCollectionChanged += DoBuildOnChildCollectionChanged;
                    _buildOnChildCollectionChanged = true;
                }
                else if( !value && _buildOnChildCollectionChanged )
                {
                    Data.OnChildCollectionChanged -= DoBuildOnChildCollectionChanged;
                    _buildOnChildCollectionChanged = true;
                }
            }
        }

        public bool IsVirtual => Data.IsVirtual;

        #endregion Public Properties

        #region C-Tor

        public Item( RedTreeItem parent, Database.Record data, string customIcon = null, bool buildOnChildCollectionChanged = true )
            : base( parent, data.Name, null, null, data.HasNonVirtualChildren )
        {
            Data = data;
            _customIcon = customIcon;
            RefreshIcon();

            ChildrenView.SortDescriptions.Add( new SortDescription( "IsVirtual", ListSortDirection.Descending ) );
            ChildrenView.SortDescriptions.Add( new SortDescription( "Title", ListSortDirection.Ascending ) );

            Data.PropertyChanged += Data_PropertyChanged;
            Data.OnChildDeleted += Data_OnChildDeleted;
            BuildOnChildCollectionChanged = buildOnChildCollectionChanged;

            PropertyChanged += Item_PropertyChanged;
        }

        private void RefreshIcon()
        {
            string icon = ( _customIcon != null ) ? _customIcon : Data.Icon;
            if ( icon != null && icon != _currentIcon )
            {
                object canvasObj = Application.Current.TryFindResource( icon );
                if ( canvasObj != null )
                {
                    Image = (Canvas)canvasObj;
                    _currentIcon = icon;
                }
            }
        }

        #endregion C-Tor

        #region LazyHierarchicalItem implementation

        protected async override Task<Core.Result> FetchAndValidateAsync()
        {
            await Data.FetchOnceSafe();

            return Core.Result.Ok;
        }

        protected override Task EnumerateChildrenAsync( IRangedCollection<DataItem> children )
        {
            children.Clear();

            // Add real children
            foreach ( Database.Record child in Data.Children )
            {
                Item item = new Item( this, child );
                children.Add( item );
            }

            // Display children queued for deletion
            foreach ( Database.Record child in Data.Deleted )
            {
                Item item = new Item( this, child, "appbar_close" );
                children.Add( item );
            }

            if ( SelectedData == Data )
            {
                SelectedItem = this;
            }

            return Task.CompletedTask;
        }

        public async override void OnSelect()
        {
            if( !Data.HasNonVirtualChildren )
            {
                await RebuildAsync( false, false );
            }
        }

        #endregion LazyHierarchicalItem implementation

        #region Drag and Drop

        public override IDataObject GetDraggedData()
        {
            if ( Data.Metadata.CanCopy )
            {
                Item parentTreeItem = Parent as Item;

                Database.Record.DraggedRecord DraggedData = new Database.Record.DraggedRecord()
                {
                    Record = Data,
                    Parent = ( parentTreeItem != null)? parentTreeItem.Data : null
                };

                return new DataObject( Data.GetType(), DraggedData );
            }

            return null;
        }

        public override bool AcceptsDraggedData( IDataObject obj )
        {
            return Data.AcceptsDraggedData( obj );
        }

        public override void DropDraggedData( IDataObject obj )
        {
            if ( AcceptsDraggedData( obj ) )
            {
                Data.PerformDrop( obj );
            }
        }

        #endregion Drag and Drop

        #region Event Handlers
        
        private async void DoBuildOnChildCollectionChanged( Database.Record affectedRecord )
        {
            await RebuildAsync( false, false );
        }

        /// <summary>
        /// This Event handler updates the tree whenever a database record's name is changed
        /// </summary>
        private void Data_PropertyChanged( object sender, PropertyChangedEventArgs e )
        {
            if ( e.PropertyName == "Name" )
            {
                Title = _data.Name;
            }

            RefreshIcon();
        }

        private async void Data_OnChildDeleted( Database.Record affectedRecord )
        {
            // When a child is deleted, we need to also delete it's children and so on and so forth
            // So we need to make sure we've expanded the tree all the way down for the deleted record
            foreach( DataItem child in Children )
            {
                Item item = child as Item;
                if( item != null )
                {
                    if ( item.Data == affectedRecord )
                    {
                        await item.RebuildAsync( true, false );
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// This event handler keeps track of the currently selected tree node
        /// </summary>
        /// <remarks>
        /// Tree Items are constantly being recreated, but the actual database records
        /// remain in memory, so we store the record of the selected tree item and use
        /// that next time there is a rebuild to figure out which item to mark selected
        /// </remarks>
        private void Item_PropertyChanged( object sender, PropertyChangedEventArgs e )
        {
            if ( e.PropertyName == "IsSelected" && IsSelected == true )
            {
                SelectedData = _data;
            }
        }

        #endregion Event Handlers
    }

    public class Root : RedTreeItem
    {
        #region Public Properties

        public List<Item> Items { get; set; }

        #endregion Public Properties

        #region C-Tor

        public Root()
            : base( null, null, null, null )
        {
            Items = new List<Item>();
        }

        #endregion C-Tor

        #region LazyHierarchicalItem implementation

        /// <summary>
        /// Override the LazyHierarchicalItem rebuild function so that any time a structural change
        /// needs to be taken into account, we can remember which tree item the user was working on
        /// </summary>
        public new async Task RebuildAsync( bool recursive, bool forceFetch )
        {
            await base.RebuildAsync( recursive, forceFetch );

            MaintainSelection();
        }

        private void MaintainSelection()
        {
            if ( Item.SelectedItem != null )
            {
                System.Diagnostics.Debug.Print( "Selecting: " + Item.SelectedItem.Title );

                Item.SelectedItem.IsExpanded = true;
                Item.SelectedItem.IsSelected = true;
            }
        }

        protected override Task EnumerateChildrenAsync( IRangedCollection<DataItem> children )
        {
            children.ResetWith( Items );
            return Task.CompletedTask;
        }

        #endregion LazyHierarchicalItem implementation
    }
}
